package com.araaya.eslimsmobile;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.araaya.eslimsmobile.gps.ListenLocation;
import com.araaya.eslimsmobile.util.ElandConstant;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;

public class LocationMapper extends FragmentActivity implements 
		GooglePlayServicesClient.ConnectionCallbacks,
		GooglePlayServicesClient.OnConnectionFailedListener,
		LocationListener,
		OnMapClickListener,
		OnMarkerClickListener{
	
	private static final double RADIUS  = 6378.137; // Radius of earth in KM
	
	private static final int UPDATE_INTERVAL_IN_SECONDS = 0;// 5
	private static final int FASTEST_INTERVAL_IN_SECONDS =0;// 1
	private static final int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
	
	private static final long UPDATE_INTERVAL = 1000 * UPDATE_INTERVAL_IN_SECONDS;
	private static final long FASTEST_INTERVAL = 1000 * FASTEST_INTERVAL_IN_SECONDS;
	
	private LocationClient locationClient;
	private LocationRequest locationRequest;
	private PolylineOptions polylineOptions;
	private Polyline lastDrawnPolyline;
	private GoogleMap googleMap;
	private LocationReceiver locationReceiver;
	private LocationWatcher locationWatcher;
	private Location lastUpdatedLocation;
	//private Location lastCapturedLocation;
	private LatLng prevLatLngPoint;
	
	private static ProgressDialog progressDialog;
	
	//private Button buttonAddPoint;
	private Button buttonMarkPoint;
	private Button buttonFinish;
	//private Button buttonPause;
	
	private TextView SatelliteView;
	private TextView MapView;
	
	private boolean isUpdatesRequested;
	private boolean isObserveInitiated;
	private boolean isTouchEnabled;
	private boolean isMultipleLocationSelect;
	private boolean isWideAreaMode;
	
	
	private SharedPreferences sharedPrefs;
	private Editor prefEditor;
	
	//private static ArrayList<HashMap<String, String>> pointList;
	
	private Marker lastLocationMarker;
	private List<Marker> addedMarkerList = new ArrayList<Marker>();
	private List<Marker> tempMarkerList;
	//private Polyline polyLine;
	
	
	/**
	 * Start up application
	 */
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		setContentView(R.layout.map_view_point1);
		
		//buttonAddPoint = (Button) findViewById(R.id.btnAddMark);
		buttonMarkPoint = (Button) findViewById(R.id.btnStartMarking);
		buttonFinish = (Button) findViewById(R.id.btnFinishMark);
		//buttonPause = (Button) findViewById(R.id.btnHoldMonitoring);//: this button to be added to start/stop GPS location update 
		MapView = (TextView) findViewById(R.id.txtMapViewType);
		SatelliteView = (TextView) findViewById(R.id.txtSatelliteViewType);
		
		if(googleMap==null){
			googleMap=((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
		}
		googleMap.setMyLocationEnabled(true);
		googleMap.getUiSettings().setMyLocationButtonEnabled(true);
		googleMap.getUiSettings().setCompassEnabled(true);
		googleMap.setOnMapClickListener(this);
		googleMap.setOnMarkerClickListener(this);
		
		locationClient = new LocationClient(this, this, this);
		locationRequest = LocationRequest.create().setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
												  .setInterval(UPDATE_INTERVAL)
												  .setFastestInterval(FASTEST_INTERVAL);
		
		locationReceiver = new LocationReceiver(); // Observable for location updates
		
		polylineOptions = new PolylineOptions().color(Color.RED).width(4);
		
		if(KmlHome.pointList.size()>0){
			for (HashMap<String, String> kmlMap : KmlHome.pointList) {
				LatLng currLatLngPoint = new LatLng(Double.parseDouble(kmlMap.get("lat")),
						Double.parseDouble(kmlMap.get("lon")));
				
				// Place Marker Point
				lastLocationMarker = googleMap.addMarker(new MarkerOptions().position(currLatLngPoint));
				addedMarkerList.add(lastLocationMarker);	
				
				// Draw Polyline
				if(prevLatLngPoint != null){
					lastDrawnPolyline = googleMap.addPolyline(polylineOptions.add(prevLatLngPoint,currLatLngPoint));
				}
				
				prevLatLngPoint = currLatLngPoint;

			}
			
			CameraPosition position = new CameraPosition.Builder().target(prevLatLngPoint).zoom(30)
					.build();
			googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(position));
		}
		
		
		/*
		 * Not used currently 
		 * 
		 * isUpdatesRequested = false;
		 * sharedPrefs = getSharedPreferences("SharedPreferences", Context.MODE_PRIVATE);
		 * prefEditor = sharedPrefs.edit();
		 * 
		 **/
		
		// Layout Listeners
		
		/*
		 * Take the current location as the location point for the map
		 * Note: Currently deprecated functionality
		 */
		/*buttonAddPoint.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
					setPoints();
			}
		});*/
		
		/*
		 * Analytically determines the most accurate location point for the map
		 */
		buttonMarkPoint.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
					if(!isGPSEnabled()){
						
						Log.d("eSlims", "Staring GPS");
						new Thread(){
							 public void run() {
								 turnGPSOn(LocationMapper.this); // Turn on GPS in a separate thread 
						     };
						 }.start();
						 
						progressDialog = new ProgressDialog(LocationMapper.this);
						progressDialog.setTitle("Wait for GPS");
						progressDialog.setMessage("GPS turning on...");
						progressDialog.setCancelable(false);
						progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
						    @Override
						    public void onClick(DialogInterface dialog, int which) {
						        dialog.dismiss();
						        locationWatcher.cancel(true);
						    }
						});
						progressDialog.show();
						
						new Handler().postDelayed(new Runnable() {
						    @Override
						    public void run() {
						        // Check if GPS enabled after after 3s = 3000ms
						    	if(isGPSEnabled()){
						    		 initLocationCapture();
						    	}else{
						    		Toast.makeText(getApplicationContext(), "GPS Could not be enabled by eSlims", Toast.LENGTH_LONG).show();
						    		progressDialog.dismiss();
						    		progressDialog = null;
						    	}
						    }
						}, 3000);	
					}else{
						Log.d("eSlims", "GPS Enabled");
						initLocationCapture();
					}				 
			}
		});
		
		/*
		 * Finish the point capture
		 */
		buttonFinish.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				if(!isMultipleLocationSelect){
					finishMap();
				}else{
					enableMultipleSelectionMode(false);
				}
			}
		});
		
		/*final LocationListener loc = (LocationListener)this;
		final Context context = this;
		buttonPause.setOnClickListener(new View.OnClickListener() {	
			@Override
			public void onClick(View v) {
				 if(locationClient.isConnected()){
					 locationClient.removeLocationUpdates(loc);
					 locationClient.disconnect(); 
					 turnGPSOff(context);
					 buttonPause.setText("Start");
				 }else{
					 buttonPause.setText("Pause");
					 
					 new Thread(){
						 public void run() {
							 turnGPSOn(context); // Turn on GPS in a separate thread 
					     };
					 }.start();
					 
					 new Handler().postDelayed(new Runnable() {
						    @Override
						    public void run() {
						        // Try to connect Location Client after 5s = 5000ms
						    	locationClient.connect();
						    }
						}, 5000);
				 }			
			}
		});*/
		
		// click the map type
		MapView.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				MapView.setTextColor(Color.WHITE);
				SatelliteView.setTextColor(Color.BLACK);
				googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
				
			}
		});

		// click the Satellite type
		SatelliteView.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				SatelliteView.setTextColor(Color.WHITE);
				MapView.setTextColor(Color.BLACK);
				googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);

			}
		});
		
		// Service Status Notifications
		this.validateServiceStatus();
	};
	
	
	private void initLocationCapture(){
		isObserveInitiated = true;
		locationWatcher = new LocationWatcher(); 
		locationReceiver.addObserver(locationWatcher);
		locationWatcher.execute(ElandConstant.LOCATION_SAMPLES); // Obtain 10 samples by default
		
		 new Handler().postDelayed(new Runnable() {
			    @Override
			    public void run() {
			        // Stop location watcher and GPS if its running more than 20s = 20000ms
			    	// progressDialog.setTitle("Process time out");
					// progressDialog.setMessage("Try again...");
			    	Log.d("eSlims", "Process Cancelled");
			    	locationWatcher.cancel(true);
			    	if(isWideAreaMode){
			    		//turnGPSOff(getApplicationContext());
			    	}
			    }
			}, 30000);	
	}
	
	/**
	 * Make Notifications about Unavailability of Required Services of the Application
	 */
	private void validateServiceStatus() {
		ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
		
		NetworkInfo mobile = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
		NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
		
		if(!isGPSEnabled() && !(mobile.isAvailable() || wifi.isAvailable())){	
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage("Switch ON GPS and Data connection") .setTitle("Warning").setCancelable(true);
			builder.setPositiveButton("OK",null);
			AlertDialog dialog = builder.create();
			dialog.show();
		}else if(!(mobile.isAvailable() || wifi.isAvailable())){
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage("Switch on Data connection") .setTitle("Warning").setCancelable(true);
			builder.setPositiveButton("OK",null);
			AlertDialog dialog = builder.create();
			dialog.show();
		}else if(!isGPSEnabled()){
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage("Switch ON GPS connection") .setTitle("Warning").setCancelable(true);
			builder.setPositiveButton("OK",null);
			AlertDialog dialog = builder.create();
			dialog.show();
		}
		
	}

	/**
	 * When pausing the application stores the state of the location updates 
	 */
	@Override
	protected void onPause() {
		/*prefEditor.putBoolean("KEY_UPDATES_ON", isUpdatesRequested);
		prefEditor.commit();*/
		super.onPause();
	}
	
	/**
	 * When resuming to application
	 * 
	 * Checks whether location updates enabled when application paused
	 */
	@Override
	protected void onResume() {
		/*if(sharedPrefs.contains("KEY_UPDATES_ON")){
			isUpdatesRequested = sharedPrefs.getBoolean("KEY_UPDATES_ON", false);
		}else{
			prefEditor.putBoolean("KEY_UPDATES_ON", true);
			prefEditor.commit();
		}*/
		super.onResume();
	}
	
	/**
	 * When activity is visible
	 */
	@Override
	protected void onStart() {
		super.onStart();
		isUpdatesRequested = true;
		locationClient.connect();
	}
	
	/**
	 * When activity is no longer visible
	 */
	@Override
	protected void onStop() {
		if(locationClient.isConnected()){
			locationClient.removeLocationUpdates(this);
		}
		locationClient.disconnect();
		super.onStop();
	}
	
	/**
	 * When key pressed
	 */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
			if(isMultipleLocationSelect){
				enableMultipleSelectionMode(false);
			}else{
				finishMap();
			}
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}
	
	/**
	 * Initialize the contents of the Activity's standard menu.
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.menu_mapview, menu);
		return super.onCreateOptionsMenu(menu);
	}
	
	/**
	 * When item in options menu is selected
	 */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.menuUndo:
			int pointSize = KmlHome.pointList.size();
			if (pointSize > 0) {
				undoPointAlert(ElandConstant.UNDO_POINT_MSG_STR,
						ElandConstant.UNDO_POINT_TITLE_STR, "Yes",
						R.drawable.deletepoint);
			} else {
				Toast.makeText(this, ElandConstant.UNDO_POINT_FAILD_STR,
						Toast.LENGTH_LONG).show();
			}
			return true;

		case R.id.menuAccuracy:
			setAccuracy();
			return true;
		
		case R.id.wideArea:
			Drawable iconRes;
			if(isWideAreaMode){
				iconRes = getResources().getDrawable(R.drawable.wide_area_false);
				isWideAreaMode =false;
			}else{
				iconRes = getResources().getDrawable(R.drawable.wide_area_true);
				isWideAreaMode = true;
			}
			item.setIcon(iconRes);
			return true;

		case R.id.menuFinish:
			finishMap();
			return true;
			
		case R.id.menuAddManualPoint:
			if(KmlHome.pointList.size()>=2){
				addManualPoint();
			}else{
				Toast.makeText(this, ElandConstant.MANUAL_POINT_VALIDATE_STR,
						Toast.LENGTH_LONG).show();
			}
			return true;
			
		case R.id.menuAddManualPointTouch:
			if(KmlHome.pointList.size()>=2){
				//MyItemizedOverlay.isToutch=true;
				isTouchEnabled=true;
				Log.d("Manual point touch ","manual point touch lat lon: ");
				Toast.makeText(this, ElandConstant.TOUCH_POINT_STR,
						Toast.LENGTH_LONG).show();
			}else{
				Toast.makeText(this, ElandConstant.MANUAL_POINT_VALIDATE_STR,
						Toast.LENGTH_LONG).show();
			}
			return true;	
			
		case R.id.help:
			Intent i = new Intent(getBaseContext(),
					HelpActivity.class);
			i.putExtra("from", "PointCapture");
			startActivity(i);
			return true;		
		}
		return false;
	}
	
	/**
	 * Called by Location Services if the attempt to connect with Location Services fails 
	 */
	@Override
	public void onConnectionFailed(ConnectionResult result) {
		if (result.hasResolution()) {
            try {
                // Start an Activity that tries to resolve the error
                result.startResolutionForResult(
                        this,
                        CONNECTION_FAILURE_RESOLUTION_REQUEST);
                /*
                * Thrown if Google Play services canceled the original
                * PendingIntent
                */
            } catch (IntentSender.SendIntentException e) {
                // Log the error
                e.printStackTrace();
            }
        } else {
            /*
             * If no resolution is available, display a dialog to the
             * user with the error.
             */
           showDialog(result.getErrorCode()); //showErrorDialog
        }
	}
	
	/**
	 * Called by Location Services when the request to connect the location client finishes successfully 
	 */
	@Override
	public void onConnected(Bundle connectionHint) {
		//Toast.makeText(getApplicationContext(), "Location Client Connected", Toast.LENGTH_LONG).show();
			lastUpdatedLocation = locationClient.getLastLocation();
			CameraPosition position;
			if(lastUpdatedLocation != null){
				position = new CameraPosition.Builder().target(new LatLng(lastUpdatedLocation.getLatitude(), lastUpdatedLocation.getLongitude())).zoom(20).build();
			}else{
				position = new CameraPosition.Builder().target(new LatLng(6.9344, 79.8428)).zoom(20).build();
			}
			googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(position));
			
			final LocationListener lst = (LocationListener) this;
			new Handler().postDelayed(new Runnable() {
			    @Override
			    public void run() {
			        // Request location updates after 2s = 2000ms
			    	locationClient.requestLocationUpdates(locationRequest, lst);
			    	//Toast.makeText(getApplicationContext(), "Requesting Location Updates", Toast.LENGTH_LONG).show();
			    }
			}, 2000);	
	}
	
	/**
	 * Called by Location Services if the location clients connection dropped due to an error 
	 */
	@Override
	public void onDisconnected() {
		// TODO Auto-generated method stub
	}
	
	/**
	 * Called when location changed
	 * @param location
	 */
	@Override
	public void onLocationChanged(Location location) {
		//Toast.makeText(getApplicationContext(), "New Location : "+location.getAccuracy(), Toast.LENGTH_SHORT).show();
		this.lastUpdatedLocation = location;
		if(isObserveInitiated){
			locationReceiver.updateCurrentLocation(location);
		}
		locatePosition(location);
	}
	
	/**
	 * Called on map touch
	 * Used in manual point adding 
	 */
	@Override
	public void onMapClick(final LatLng point) {
		if(isTouchEnabled){				
		String txt = " Lat : " + point.latitude + " " + "\n Lon : " + point.longitude + " "	;
		AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
		alt_bld.setMessage(txt)
				.setCancelable(false)
				.setPositiveButton("Save",
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int id) {
								savePoint(point.longitude, point.latitude, point.latitude);
								return;
							}
						})
				.setNegativeButton("No", new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int id) {
						dialog.cancel();
						return;

					}
				});

		AlertDialog alert = alt_bld.create();
		alert.setTitle("Save Point");

		alert.show();
		isTouchEnabled=false;
		}
	}
	
	/**
	 * Triggered when clicked on a marker
	 */
	@Override
	public boolean onMarkerClick(Marker marker) {
		// TODO Go to add point dialog on click
		Log.d("GeoMapper", "marker clicked");
		if(isMultipleLocationSelect){
			LatLng l = marker.getPosition();
			capturePointAlert(l.longitude, l.latitude, 0 ,
				Float.parseFloat(marker.getTitle()) ,
				ElandConstant.ADD_POINT_SUCCESS_ASK_STR,
				"Capture Point", "YES", R.drawable.ok);
			return true;
		}else{
			return false;
		}
	}

	/**
	 * Turn on the devices GPS radio
	 * Warning : This is deprecated in the Android 4.0 versions and above
	 * @param context
	 */
	@SuppressWarnings("deprecation")
	public static void turnGPSOn(Context context)
	{
	    Intent intent = new Intent("android.location.GPS_ENABLED_CHANGE");
	    intent.putExtra("enabled", true);
	    context.sendBroadcast(intent);

	    String provider = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
	    if (! provider.contains("gps"))
	    { //if gps is disabled
	        final Intent poke = new Intent();
	        poke.setClassName("com.android.settings", "com.android.settings.widget.SettingsAppWidgetProvider"); 
	        poke.addCategory(Intent.CATEGORY_ALTERNATIVE);
	        poke.setData(Uri.parse("3")); 
	        context.sendBroadcast(poke);
	    }
	}
	
	/**
	 * Turn off the devices GPS radio
	 * Warning : This is deprecated in the Android 4.0 versions and above
	 * @param context
	 */
	@SuppressWarnings("deprecation")
	public static void turnGPSOff(Context context)
	{
	    String provider = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
	    if (provider.contains("gps"))
	    { //if gps is enabled
	        final Intent poke = new Intent();
	        poke.setClassName("com.android.settings", "com.android.settings.widget.SettingsAppWidgetProvider");
	        poke.addCategory(Intent.CATEGORY_ALTERNATIVE);
	        poke.setData(Uri.parse("3")); 
	        context.sendBroadcast(poke);
	    }
	}
	
	/**
	 * Locates the current location on the map according to the latest location update received 
	 * @param l Last location
	 */
	private void locatePosition(Location l){
		float zoom = 0;
		LatLng currLoc = new LatLng(l.getLatitude(), l.getLongitude());
		CameraPosition camPos =  googleMap.getCameraPosition();
    	if(camPos != null){
    		 zoom=camPos.zoom;
    	}
		CameraPosition position = new CameraPosition.Builder().target(currLoc).zoom(zoom).build(); 
		googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(position));
	}
	
	/**
	 * LocationWatcher asynchronously executed and
	 * LocationWatcher class observes the LocationReceiver observable for any changes.
	 */
	
	private class LocationWatcher extends AsyncTask<Integer, Integer, Long> implements Observer {
		
		ProgressBar progressBar;
		int locationCount = 0;
		List<Location> locationList = new ArrayList();
		private boolean isCancelled = false;
		public LocationWatcher(){

		}
		
		@Override
		public void update(Observable arg0, Object location) {
			Location loc = (Location) location;
			Log.d("eSlims", "Maintaining Accuracy : "+ KmlHome.ACCURACY_LEVEL);
			if(loc.getAccuracy()<KmlHome.ACCURACY_LEVEL){ // Default accuracy is 10M
				locationList.add(loc);
				locationCount--;
			}
		}
		
		@Override
		protected void onCancelled() {
			isCancelled = true;
			progressDialog.dismiss();
	    	progressDialog = null;
		}

		@Override
		protected Long doInBackground(Integer... args) {
			locationCount = args[0];
			int temp = args[0];
			while(locationCount>0){
				// loop until 10 samples 
				// TODO: define a time limit for sampling most probably two or three min's maximum
				if (isCancelled){
					break;
				}
			}
			
			locationReceiver.deleteObservers();
			isObserveInitiated=false;
			
			return (long) locationCount;
		}
		
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			if(progressDialog != null){
				progressDialog.setTitle("Wait for Location");
				progressDialog.setMessage("Capturing points...");
			}else{
				progressDialog = new ProgressDialog(LocationMapper.this);
				progressDialog.setTitle("Wait for location");
				progressDialog.setMessage("Capturing points...");
				progressDialog.setCancelable(false);
				progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
				    @Override
				    public void onClick(DialogInterface dialog, int which) {
				        dialog.dismiss();
				        locationWatcher.cancel(true);
				    }
				});
				progressDialog.show();
			}
			//
	    	//progressBar = new ProgressBar(LocationMapper.this);
		}
		
		@Override
		protected void onPostExecute(Long result) {
			if(result==0){
				Log.d("GeoMapper", "---- Finished Sampling ----");
				// Do insertion sort for location list
				Location[] locArr = locationList.toArray(new Location[0]);
				
				for(int i=0;i<10; i++){
					Location tempLoc = locArr[i];
					int j=i;
					while(j>0 && locArr[j-1].getAccuracy()>tempLoc.getAccuracy()){
						locArr[j] = locArr[j-1];
						j--;
					}
					locArr[j] =tempLoc;
				}
				
				Log.d("GeoMapper", "--- Locations Sorted ---");
				// Print Sorted Location List
				
				/*for(Location l : locArr){
					Log.d("GeoMapper", "Sorted Sample Accuracy : "+l.getAccuracy());
				}*/
				
				List<Location> finalLocations = new ArrayList<Location>();
				Location temp = locArr[0];
				finalLocations.add(temp);
				Location tempNext;
				double dist=0;
				/*
				 * If distance is different than two(need to decide) meters consider them as separate locations
				 */
				for(int i = 1; i< locArr.length; i++){
					tempNext = locArr[i];
					if(temp.getAccuracy()==tempNext.getAccuracy()){
						dist = measureDistance(temp, tempNext);
						Log.d("GeoMapper", "Location Distance : "+dist);
						if(dist>ElandConstant.MINIMUM_POINT_DISTANCE_DIFFERENCE){ // Default distance is 2 meters
							finalLocations.add(tempNext);
							temp = tempNext;
							dist = 0;
						}
					}else{ // IF accuracy is different
						dist = measureDistance(temp, tempNext);
						Log.d("GeoMapper", "Location Distance : "+dist);
						if(dist>ElandConstant.MINIMUM_POINT_DISTANCE_DIFFERENCE){ 
							finalLocations.add(tempNext);
						}
						temp = tempNext;
					}
				}

				if(finalLocations.size()>1){
					enableMultipleSelectionMode(true);
					tempMarkerList = new ArrayList<Marker>();
					Marker tempMarker = null;
					
					for(Location l : finalLocations){
						Log.d("GeoMapper", "Selected List Accuracy : "+l.getAccuracy());
						tempMarker = googleMap.addMarker(new MarkerOptions()
						.position(new LatLng(l.getLatitude(), l.getLongitude()))
						.title(String.valueOf(l.getAccuracy())));
						tempMarkerList.add(tempMarker);
					}
					Toast.makeText(getApplicationContext(), "Multiple Locations Discovered.\nPlease Select Preffered Location", Toast.LENGTH_LONG).show();
				}else{
					Location l = finalLocations.get(0);
					capturePointAlert(l.getLongitude(), l.getLatitude(), l.getAltitude(),
							l.getAccuracy(),
							ElandConstant.ADD_POINT_SUCCESS_ASK_STR,
							"Capture Point", "YES", R.drawable.ok);
				}
			}
			
			progressDialog.dismiss();
			progressDialog = null;
			if(isWideAreaMode){
				//turnGPSOff(getApplicationContext());
			}
			super.onPostExecute(result);
		}

	}
	
	/**
	 * Set visibility of the currently added markers in the map
	 * @param b true to set visible false to set invisible
	 */
	private void setMarkerListVisible(boolean b) {
		for(Marker m : addedMarkerList){
			m.setVisible(b);
		}
	}
	
	/**
	 *Set visibility of button set
	 */
	private void setButtonsVisible(int state) {
		//buttonAddPoint.setVisibility(state);
		buttonMarkPoint.setVisibility(state);
		//buttonPause.setVisibility(state);
	}
	
	/**
	 * Returns the distance between two locations in meters
	 * @param l Location
	 * @return distance in meters
	 */
	private double measureDistance(Location l1, Location l2){
	    double dLat = (l1.getLatitude() - l2.getLatitude()) * Math.PI / 180;
	    double dLon = (l1.getLongitude() - l2.getLongitude()) * Math.PI / 180;
	    double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
	    Math.cos(l2.getLatitude() * Math.PI / 180) * Math.cos(l1.getLatitude() * Math.PI / 180) *
	    Math.sin(dLon/2) * Math.sin(dLon/2);
	    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
	    double d = RADIUS * c;
	    return d * 1000; // meters
	}
	
	/**
	 *  =============================================================================== 
	 * 	 Copied methods from old implementation
	 *  ===============================================================================
	 **/
	
	/*class checkAyscTask implements Runnable {
	    AsyncTask<Integer, Integer, Long> mAT;
	    Context context;

	    public checkAyscTask(AsyncTask<Integer, Integer, Long> at) {
	        mAT = at;
	    }

	    @Override
	    public void run() {
	        mHandler.postDelayed(runnable, 40000);
	        Log.d("Post delayed", "Post delayed 6000");
	        // After 60sec the task in run() of runnable will be done
	    }
	    Handler mHandler = new Handler();
	    Runnable runnable = new Runnable() {
	        @Override
	        public void run() {
	        	 Log.d("Post delayed", "Post delayed 6001: "+mAT.getStatus());
	            if (mAT.getStatus() == Status.RUNNING || mAT.getStatus() == Status.PENDING) {
	            	 Log.d("Post delayed", "Post delayed 6002: "+mAT.getStatus());
	            	mAT.cancel(true); //Cancel Async task or do the operation you want after 1 minute
	            }
	        }
	    };
	}*/
	
	private void finishMap() {
		//	MyItemizedOverlay.isToutch=false;
		if (KmlHome.pointList.size() > 2) {
			finishAlert();
		} else {
			displayAlertFinish(ElandConstant.FINISH_MAP_ALERT, "Exit Map",
						"Yes", R.drawable.warning);
		}
	}

	private void setAccuracy() {
		AlertDialog.Builder alert = new AlertDialog.Builder(this);
		alert.setTitle("Accuracy Level");
		alert.setMessage("Accuracy Level : " + KmlHome.ACCURACY_LEVEL + " m"
					+ "\nEnter the New Accuracy Level...");

		// Set an EditText view to get user input
		final EditText input = new EditText(this);
		alert.setView(input);

		alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int whichButton) {
				String value = input.getText().toString();
				try {
					if (value != null || value.length() > 0) {
						KmlHome.ACCURACY_LEVEL = Integer.parseInt(value);
					} else {
						Toast.makeText(LocationMapper.this,
									"Plaese Enter Valid Number", Toast.LENGTH_LONG)
									.show();
					}

				} catch (Exception e) {
					e.printStackTrace();
					Toast.makeText(LocationMapper.this,
							"Plaese Enter Valid Number", Toast.LENGTH_LONG)
							.show();
				}
			}
		});

		alert.setNeutralButton("Reset", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id) {
				KmlHome.ACCURACY_LEVEL = ElandConstant.DEFAULT_ACCURACY_LEVEL;
					return;
				}
			});

		alert.setNegativeButton("Cancel",
				new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog, int whichButton) {
							dialog.cancel();
						}
					});
			
			alert.show();
		}

	// Message Alert
	private void displayAlert(String message, String title, String btnTxt,
				Integer image) {

		AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
		alt_bld.setMessage(message)
			.setCancelable(false)
			.setPositiveButton(btnTxt,
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog, int id) {
									dialog.cancel();
									return;
								}
							});

		AlertDialog alert = alt_bld.create();
			// Title for AlertDialog
		alert.setTitle(title);
			// Icon for AlertDialog
		alert.setIcon(image);
		alert.show();
	}

	// Message Alert
	private void displayAlertFinish(String message, String title, String btnTxt,
				Integer image) {
		AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
		alt_bld.setMessage(message)
				.setCancelable(false)
				.setPositiveButton(btnTxt,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int id) {
								finish();
								return;
							}
						})
				.setNegativeButton("No", new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int id) {
						dialog.cancel();
						return;
					}
				});

		AlertDialog alert = alt_bld.create();
			// Title for AlertDialog
		alert.setTitle(title);
			// Icon for AlertDialog
		alert.setIcon(image);
		alert.show();
	}
	
	// Undo point Alert
	private void undoPointAlert(String message, String title, String btnTxt,
				Integer image) {

		AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
		alt_bld.setMessage(message)
				.setCancelable(false)
				.setPositiveButton(btnTxt,
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog, int id) {
							Log.d("kml size","kml before; "+KmlHome.pointList.size());
							
							lastLocationMarker.remove();
							lastDrawnPolyline.remove();
							
							KmlHome.pointList.remove(KmlHome.pointList.size() - 1);	
							return;
						}
					})
					.setNegativeButton("No", new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog, int id) {
							dialog.cancel();
							return;
						}
					});

		AlertDialog alert = alt_bld.create();
			// Title for AlertDialog
			alert.setTitle(title);
			// Icon for AlertDialog
			alert.setIcon(image);
			alert.show();
		}

		// Capture Point Success Alert
		private void capturePointAlert(final double lon, final double lat,
				final double alt, final double accuracy, String message,
				String title, String btnTxt, Integer image) {
			String txt = " Lat : " + lat + " " + "\n Lon : " + lon + " "
					+ "\n Accuracy : " + accuracy                   // "\n Alt : " + alt + "  " +
					+ "\n\n " + message;
			AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
			alt_bld.setMessage(txt)
					.setCancelable(false)
					.setPositiveButton(btnTxt,
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog, int id) {
									savePoint(lon, lat, alt);
									return;
								}
							})
					.setNegativeButton("No", new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog, int id) {

							dialog.cancel();
							return;

						}
					});

			AlertDialog alert = alt_bld.create();
			// Title for AlertDialog
			alert.setTitle(title);
			// Icon for AlertDialog
			alert.setIcon(image);
			alert.show();
		}

		// finish Alert
		private void finishAlert() {
			AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
			alt_bld.setMessage(ElandConstant.FINISH_MAP_STR)
					.setCancelable(false)
					.setPositiveButton("Yes",
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog, int id) {
									finish();
									Intent i = new Intent(getBaseContext(),
											NewLand.class);
									startActivity(i);
								}
							})
					.setNegativeButton("No", new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog, int id) {
							// Action for 'NO' Button
							dialog.cancel();
						}
					});
			AlertDialog alert = alt_bld.create();
			alert.show();
		}
		
		private boolean savePoint(double longitude, double lattitude, double altitude) {

			if (longitude > 0 && lattitude > 0) {
				
				// Add point to captured point list as map
				HashMap<String, String> map = new HashMap<String, String>();
				map.put("lon", "" + longitude);
				map.put("lat", "" + lattitude);
				map.put("alt", "" + altitude);
				KmlHome.pointList.add(map);
				
				LatLng currLatLngPoint = new LatLng(lattitude, longitude);
				
				// Place Marker Point 
				lastLocationMarker = googleMap.addMarker(new MarkerOptions().position(currLatLngPoint));
				addedMarkerList.add(lastLocationMarker);
				 
				if(isMultipleLocationSelect){ // exit if it was a multiple location selection mode
					enableMultipleSelectionMode(false);
				}
				
				// Draw Polyline
				if(prevLatLngPoint != null){
					lastDrawnPolyline = googleMap.addPolyline(polylineOptions.add(prevLatLngPoint,currLatLngPoint));
				}
				
				prevLatLngPoint = currLatLngPoint;
				
				return true;
			} else {
				displayAlert(ElandConstant.NO_GPS_SIGNAL_STR,
						ElandConstant.GPS_TITLE_STR, "OK", R.drawable.wrong);
				return false;
			}

		}
		
		private void enableMultipleSelectionMode(boolean a){
			int buttonVisible;
		    boolean markerVisible;
			if(a){
				buttonVisible = View.INVISIBLE;
				markerVisible = false;
				isMultipleLocationSelect = true;
			}else{
				Toast.makeText(getApplicationContext(), "Exiting from Multiple Location Select Mode", Toast.LENGTH_LONG).show();
				buttonVisible = View.VISIBLE;
				markerVisible = true;
				
				for(Marker m : tempMarkerList){
					m.remove();
				}
				isMultipleLocationSelect = false;
			}
			
			setMarkerListVisible(markerVisible);
			setButtonsVisible(buttonVisible);
		}
		
		private void setPoints() {
			try {
				if (isGPSEnabled()) {
					double longitude = lastUpdatedLocation.getLongitude();
					double lattitude = lastUpdatedLocation.getLatitude();
					double altitude = lastUpdatedLocation.getLongitude();
					double accuracy = lastUpdatedLocation.getAccuracy();

					Log.d("Accuracy", "Accuracy "+accuracy+"longitude: "+longitude+"lattitude: "+lattitude);
					
					if (longitude > 0 && lattitude > 0) {
						if (accuracy < KmlHome.ACCURACY_LEVEL) {
							capturePointAlert(longitude, lattitude, altitude,
									accuracy,
									ElandConstant.ADD_POINT_SUCCESS_ASK_STR,
									"Capture Point", "YES", R.drawable.ok);
						} else {
							displayAlert(ElandConstant.ACCURACY_LEVEL_STR + " ( "
									+ accuracy + " m ) \n Try again.",
									ElandConstant.ACCURACY_LEVEL_TITLE_STR, "OK",
									R.drawable.warning);
						}
					} else {
						displayAlert(ElandConstant.NO_GPS_SIGNAL_STR,
								ElandConstant.GPS_TITLE_STR, "OK", R.drawable.wrong);
					}
				} else {
					displayAlert(ElandConstant.NO_GPS_SIGNAL_STR,
							ElandConstant.GPS_TITLE_STR, "OK", R.drawable.wrong);
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		/**
		 * Checks the GPS radio is enabled in the device.
		 * @return true if GPS is enabled
		 */
		private boolean isGPSEnabled() {
			LocationManager locationmanager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
			if (locationmanager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
				return true;
			} else {
				return false;
			}
		}
		
		private void addManualPoint(){
			
			AlertDialog.Builder alert = new AlertDialog.Builder(this);

	    	alert.setTitle("Add Manual Location");
	    	alert.setMessage("Please Enter Your Lat,Lon");
	    	Log.d("Manual point ","manual point lat lon: ");

	    	// Set an EditText view to get user input
	    	LinearLayout layout=new LinearLayout(this);
	    	LinearLayout layout1=new LinearLayout(this);    	
	    	LinearLayout layout2=new LinearLayout(this);
	    	layout.setOrientation(1);
	    	
	    	final EditText inputLat = new EditText(this);
	    	final TextView tvLat=new TextView(this);
	    	tvLat.setPadding(20, 10, 30, 10);    	
	    	tvLat.setText("Lat :");  
	    	inputLat.setWidth(150);
	    	
	    	
	    	final EditText inputLon = new EditText(this);
	    	final TextView tvLon=new TextView(this);
	    	tvLon.setPadding(20, 10, 30, 10);
	    	tvLon.setText("Lon :");
	    	inputLon.setWidth(150);
	    	
	    	//set current point
	    	if (isGPSEnabled()) {

				double longitude = ListenLocation.longitude;
				double lattitude = ListenLocation.latitude;
				
				inputLat.setText(""+lattitude);
	    		inputLon.setText(""+longitude);
				
	    	}else{
	    		inputLat.setText("7.0000");
	    		inputLon.setText("79.0000");
	    	}
	    	
	    	
	    	//alert.setView(inputLat);
	    	//alert.setView(inputLon);
	    	
	    	layout1.addView(tvLat);
	    	layout1.addView(inputLat);
	    	
	    	layout2.addView(tvLon);
	    	layout2.addView(inputLon);
	    	
	    	layout.addView(layout1);
	    	layout.addView(layout2);
	    	alert.setView(layout);

	    	alert.setPositiveButton("Add", new DialogInterface.OnClickListener() {
	    	public void onClick(DialogInterface dialog, int whichButton) {
	    		String valueLat = inputLat.getText().toString();
	    		String valueLon = inputLon.getText().toString();
	    		
	    		if(valueLat.length()>0 && valueLon.length()>0){
	    			try{
	    				double lat=Double.parseDouble(valueLat);
	    				double lon=Double.parseDouble(valueLon);
	    				savePoint(lon, lat, 0);
	    			}catch (Exception e) {
	    				Toast.makeText(LocationMapper.this, "Please Enter Valid Point..", Toast.LENGTH_LONG).show();
	    				valueLat="";
	        			valueLon="";
					}	
	    		}else{
	    			valueLat="";
	    			valueLon="";
	    		}
	    	  }
	    	});

	    	alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
	    	  public void onClick(DialogInterface dialog, int whichButton) {
	    	    dialog.cancel();
	    		 
	    	  }
	    	});

	    	alert.show();	
		}
}
